package org.jadelyre.jade;

import org.jadelyre.core.AbstractJadeLyre;
import org.jadelyre.core.Banner;
import org.jadelyre.core.Handler;
import org.jadelyre.core.ThreadPool;
import org.jadelyre.core.codec.Codec;
import org.jadelyre.core.codec.sun.SunCodec;
import org.jadelyre.core.exception.JadeException;
import org.jadelyre.core.exception.JadeLyreException;
import org.jadelyre.core.worker.WorkerFactory;
import org.jadelyre.utils.Assert;
import org.jadelyre.utils.StrUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketOption;
import java.net.StandardSocketOptions;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.Map;

public class JadeServer extends AbstractJadeLyre {
    private static final Logger logger = LoggerFactory.getLogger(JadeServer.class);
    private AsynchronousServerSocketChannel asynchronousServerSocketChannel;

    public JadeServer(ServerConfig serverConfig,Handler handler) throws JadeLyreException {
        this(serverConfig,handler,new SunCodec());
    }
    public JadeServer(ServerConfig serverConfig, Handler handler, Codec codec) throws JadeLyreException {
        super(handler, codec,serverConfig);
        Assert.notNull(serverConfig,"ServerConfig is required,must not be null");
        Assert.notNull(handler,"Handler is required,must not be null");
    }

    public void start() {
        ServerConfig serverConfig = (ServerConfig) jadeLyreConfig;
        InetSocketAddress inetSocketAddress = StrUtils.isBlank(serverConfig.getHost()) ? new InetSocketAddress(serverConfig.getPort())
                : new InetSocketAddress(serverConfig.getHost(), serverConfig.getPort());
        try {
            asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open(asynchronousChannelGroup);
//            asynchronousServerSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
//            asynchronousServerSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 64 * 1024);

            asynchronousServerSocketChannel.bind(inetSocketAddress);
        } catch (IOException e) {
            throw new JadeException(e);
        }
        asynchronousServerSocketChannel.accept(this,new AcceptCompletionHandler());
        addShutdownHook();
        drawBanner();
        started = true;
    }

    @Override
    public void stop() throws JadeException {
        started = false;
        try {
            if (asynchronousServerSocketChannel.isOpen()) {
                asynchronousServerSocketChannel.close();
            }
        } catch (IOException e) {
            throw new JadeException(e);
        }
        asynchronousChannelGroup.shutdown();
    }

    private void drawBanner(){
        String draw = Banner.draw(this);
        if (logger.isInfoEnabled()) {
            logger.info(draw);
        } else {
            System.out.println(draw);
        }
    }
    public AsynchronousServerSocketChannel getAsynchronousServerSocketChannel() {
        return asynchronousServerSocketChannel;
    }

}
